local __application__ = _G.__application__
local NewLuaScriptElement = __application__.NewLuaScriptElement;

local Element = commonlib.inherit(commonlib.gettable("commonlib.ToolBase"), commonlib.gettable("commonlib.Window.Element"));

Element:Property("TextElement", false, "IsTextElement"); -- 是否是文本元素

-- 元素构造函数
function Element:ctor()
    self.m_tagname = nil;
    self.m_parent = nil;
    self.m_window = nil;
    self.m_childrens = {};
    self.m_attributes = {};
end

-- 初始化函数
function Element:Init(xmlnode, parent, window)
    self.m_tagname = xmlnode.name or "div";
    self.m_parent = parent;
    self.m_window = window;
    self.m_xmlnode = xmlnode;
    self.m_lua_script_element = NewLuaScriptElement(self.m_tagname);
    self:SetTextElement(self.m_tagname == "text");

    -- 加载属性
    for attr_key, attr_value in pairs(xmlnode.attr or {}) do
        self:SetAttributeValue(attr_key, attr_value);
    end

    return self;
end

-- 获取父元素
function Element:GetParent()
    return self.m_parent;
end

-- 获取窗口
function Element:GetWindow()
    return self.m_window;
end

-- 获取标签名
function Element:GetTagName()
    return self.m_tagname;
end

-- 设置标签名
function Element:SetTagName(tagname)
    self.m_tagname = tagname;
    self:GetLuaScriptElement():SetTagName(tagname);
end

-- 设置可见性
function Element:SetVisible(visible)
    self.m_visible = visible;
    self:GetLuaScriptElement():SetVisible(visible);
    self:GetWindow():UpdateNextTickLayoutElement(self);
end

-- 获取xmlnode文本
function Element:GetXmlNodeText(xmlnode)
    if (type(xmlnode) == "string") then
        return xmlnode
    end

    local text = "";
    for i, sub_xmlnode in ipairs(xmlnode) do
        text = text .. self:GetXmlNodeText(sub_xmlnode);
    end
    return text;
end

-- 获取Lua脚本元素
function Element:GetLuaScriptElement()
    return self.m_lua_script_element;
end

-- 属性值更新
function Element:OnAttributeChanged(attrName, attrValue)
    if (not self:GetWindow() or not self:GetWindow():IsVisible()) then return end
    if (attrName == "style" and attrName == "class") then
        self:GetWindow():UpdateNextTickStyleElement(self);
    elseif (attrName == "text" and self:IsTextElement()) then
        self:GetLuaScriptElement():SetText(attrValue);
    end
end

-- 设置属性值
function Element:SetAttributeValue(attrName, attrValue)
    local oldAttrValue = self.m_attributes[attrName];
    self.m_attributes[attrName] = attrValue;
    self:GetLuaScriptElement():SetAttribute(attrName, attrValue);
    if (oldAttrValue ~= attrValue) then
        self:OnAttributeChanged(attrName, attrValue);
    end
end

-- 获取属性集
function Element:GetAttributes()
    return self.m_attributes;
end

-- 获取属性值
function Element:GetAttributeValue(attrName, defaultValue)
    local attrValue = self.m_attributes[attrName];
    if (attrValue == nil) then
        return defaultValue
    end
    return attrValue;
end

-- 获取数字属性值
function Element:GetAttributeNumberValue(attrName, defaultValue)
    return tonumber(self:GetAttributeValue(attrName)) or defaultValue or 0;
end

-- 获取字符串属性值
function Element:GetAttributeStringValue(attrName, defaultValue)
    local value = self:GetAttributeValue(attrName)
    return (value ~= nil and tostring(value) or defaultValue) or "";
end

-- 获取布尔属性值
function Element:GetAttributeBoolValue(attrName, defaultValue)
    local value = self:GetAttributeValue(attrName);
    if (type(value) == "boolean") then
        return value
    end
    if (type(value) ~= "string") then
        return defaultValue
    end
    return value == "true";
end

-- 获取表属性值
function Element:GetAttributeTableValue(attrName, defaultValue)
    local value = self:GetAttributeValue(attrName);
    return (type(value) == "table" and value or defaultValue) or {};
end

-- 获取函数属性
function Element:GetAttributeFunctionValue(attrName, defaultValue)
    local value = self:GetAttributeValue(attrName);
    if (type(value) == "function") then
        return value
    end
    if (type(value) == "string") then
        value = commonlib.loadstring(value, self:GetG());
        self:SetAttrValue(attrName, value); -- 编译缓存
        return value or defaultValue;
    end
    return defaultValue;
end

-- 添加子元素
function Element:AddChildren(children)
    if (not children) then
        return
    end
    table.insert(self.m_childrens, children);
    self:GetLuaScriptElement():AppendChildren(children:GetLuaScriptElement());
end

-- 判断子元素是否存在
function Element:IsExistChildren(children)
    if (not children) then
        return false
    end
    for index, child in ipairs(self.m_childrens) do
        if (child == children) then
            return true, index
        end
    end
    return false;
end

-- 获得子元素
function Element:GetChildren(index)
    return self.m_childrens[index];
end

-- 移除子元素
function Element:RemoveChildren(children)
    local exist, index = self:IsExistChildren(children);
    if (exist) then
        table.remove(self.m_childrens, index)
        self:GetLuaScriptElement():RemoveChildren(children:GetLuaScriptElement());
    end
end

-- 上一个兄弟元素
function Element:GetPrevSibling()
    local parent = self:GetParent();
    if (not parent) then
        return
    end
    local prev_sibling = nil;
    for _, element in ipairs(parent.m_childrens) do
        if (element == self) then
            return prev_sibling
        end
        prev_sibling = element;
    end
end

-- 下一个兄弟元素
function Element:GetNextSibling()
    local parent = self:GetParent();
    if (not parent) then
        return
    end
    local next_sibling = nil;
    for i = #parent.m_childrens, 1, -1 do
        local element = parent.m_childrens[i];
        if (element == self) then
            return next_sibling
        end
        next_sibling = element;
    end
end

-- 获取元素位置
function Element:GetChildrenIndex(target_children)
    for index, children in ipairs(self.m_childrens) do
        if (children == target_children) then
            return index
        end
    end
    return 0;
end

-- 遍历子元素
function Element:ForEachChildren(callback)
    for _, children in ipairs(self.m_childrens) do
        callback(children);
    end
end

-- 是否是祖先元素
function Element:IsAncestor(el)
    local cur_el = self:GetParent();
    while (cur_el ~= nil and el ~= nil) do
        if (cur_el == el) then
            return true;
        end
        cur_el = cur_el:GetParent();
    end
    return false;
end

-- 复制
function Element:Clone()
    local clone = Element:new():Init({
        name = self:GetTagName(),
        attr = self:GetAttributes(),
    }, self:GetParent(), self:GetWindow());

    for _, children in ipairs(self.m_childrens) do
        children:SetParent(clone);
        clone:AppendChild(children:Clone());
    end
    return clone;
end

-- 调试
function Element:Debug(indent)
    indent = indent or "";
    print(indent .. self.m_tagname);
    indent = indent .. "    ";
    for _, children in ipairs(self.m_childrens) do
        children:Debug(indent);
    end
end

return Element;
